using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.Practices.Unity.Interception
{
    [TestClass]
    public class InParametersInterfaceInterceptorTest
    {
        static TInterface WrapAndCreateType<TInterface, TConcrete>(IEnumerable<KeyValuePair<MethodBase, List<IInterceptionHandler>>> handlers)
            where TConcrete : TInterface
        {
            Type wrappedType = InterfaceInterceptor.WrapInterface(typeof(TInterface));
            ILEmitProxy proxy = new ILEmitProxy(handlers);
            object target = Activator.CreateInstance(typeof(TConcrete));
            List<object> wrappedCtorArgs = new List<object> { proxy, target };
            return (TInterface)Activator.CreateInstance(wrappedType, wrappedCtorArgs.ToArray());
        }

        [TestMethod]
        public void InReferenceParameter()
        {
            Recorder.Records.Clear();
            RecordingHandler handler = new RecordingHandler();
            MethodBase method = typeof(ISpyIn).GetMethod("InReferenceParameter");
            Dictionary<MethodBase, List<IInterceptionHandler>> dictionary = new Dictionary<MethodBase, List<IInterceptionHandler>>();
            List<IInterceptionHandler> handlers = new List<IInterceptionHandler> { handler };
            dictionary.Add(method, handlers);

            ISpyIn result = WrapAndCreateType<ISpyIn, SpyIn>(dictionary);
            result.InReferenceParameter("Hello");

            Assert.AreEqual(3, Recorder.Records.Count);
            Assert.AreEqual("Before Method", Recorder.Records[0]);
            Assert.AreEqual("In Method: Hello", Recorder.Records[1]);
            Assert.AreEqual("After Method", Recorder.Records[2]);
        }

        [TestMethod]
        public void InValueParameter()
        {
            Recorder.Records.Clear();
            RecordingHandler handler = new RecordingHandler();
            MethodBase method = typeof(ISpyIn).GetMethod("InValueParameter");
            Dictionary<MethodBase, List<IInterceptionHandler>> dictionary = new Dictionary<MethodBase, List<IInterceptionHandler>>();
            List<IInterceptionHandler> handlers = new List<IInterceptionHandler> { handler };
            dictionary.Add(method, handlers);

            ISpyIn result = WrapAndCreateType<ISpyIn, SpyIn>(dictionary);
            result.InValueParameter(42);

            Assert.AreEqual(3, Recorder.Records.Count);
            Assert.AreEqual("Before Method", Recorder.Records[0]);
            Assert.AreEqual("In Method: 42", Recorder.Records[1]);
            Assert.AreEqual("After Method", Recorder.Records[2]);
        }

        public interface ISpyIn
        {
            void InReferenceParameter(string s);
            void InValueParameter(int x);
        }

        sealed class SpyIn : ISpyIn
        {
            void ISpyIn.InReferenceParameter(string s)
            {
                Recorder.Records.Add("In Method: " + s);
            }

            public void InValueParameter(int x)
            {
                Recorder.Records.Add("In Method: " + x);
            }
        }
    }
}